#include "mainwindow.h"
#include "ui_mainwindow.h"

const int kCost1=10; //直移一格消耗
const int kCost2=14; //斜移一格消耗

int temp[2];
char arr[50][50];

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->setWindowTitle("Ai_Load");
    this->resize(1000,1024);
    this->setWindowFlag(Qt::FramelessWindowHint);
    this->setWindowFlags(Qt::Dialog | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint);

    startX = 0;
    startY = 24;
    gridW = 20;
    gridH = 20;
    paintX[0]=-1;
    paintX[1]=-1;
    paintY[0]=-1;
    paintY[1]=-1;
}


void MainWindow::mousePressEvent(QMouseEvent *)
{
    QPoint sPoint2=QCursor::pos();
    QPoint windowPoint = this->mapFromGlobal(sPoint2);
    int x = windowPoint.x();
    int y = windowPoint.y();
    //要保证点击在棋盘里
    if (x >= startX && x <= startX + 50 * gridW
            && y >= startY && y <= startY + 50 * gridH)
    {
        //棋盘的位置转换成坐标下标值
        temp[0] = (x - startX) / gridW;
        temp[1] = (y - startY) / gridH;
        arr[temp[0]][temp[1]]=1;
        update();
    }

}

void MainWindow::paintEvent(QPaintEvent *)
{
    QPainter paint(this);
    for(int i = 0; i <= 50; i++)
    {
        //横线
        paint.drawLine(startX, startY+i*gridH, startX+50*gridW, startY+i*gridH);

        //竖线
        paint.drawLine(startX+i*gridW, startY, startX+i*gridW, startY+50*gridH);
    }

    for(int i = 0;i<2;i++)
    {
        if(i==0)
        {
            paint.setPen(QPen(Qt::green));
            paint.setBrush(QBrush(Qt::green));
        }
        else
        {
            paint.setPen(QPen(Qt::red));
            paint.setBrush(QBrush(Qt::red));
        }
        if (paintX[i] != -1 && paintY[i] != -1)
        {
            paint.drawRect(startX + paintX[i] * gridW, startY + paintY[i] * gridH, gridW, gridH);
        }
    }

    for(int i = 0;i<50;i++)
    {
        for(int j = 0;j<50;j++)
        {
            if((paintX[0]==i&&paintY[0]==j)||(paintX[1]==i&&paintY[1]==j))
            {
                continue;
            }
            if(arr[i][j]==1)
            {
                paint.setPen(QPen(Qt::black));
                paint.setBrush(QBrush(Qt::black));
                paint.drawRect(startX + i * gridW, startY + j * gridH, gridW, gridH);
            }
            if(arr[i][j]==2)
            {
                paint.setPen(QPen(Qt::yellow));
                paint.setBrush(QBrush(Qt::yellow));
                paint.drawRect(startX + i * gridW, startY + j * gridH, gridW, gridH);
            }
            if(arr[i][j]==3)
            {
                paint.setPen(QPen(Qt::blue));
                paint.setBrush(QBrush(Qt::blue));
                paint.drawRect(startX + i * gridW, startY + j * gridH, gridW, gridH);
            }
        }
    }
}

void MainWindow::closeEvent(QCloseEvent *)
{
    this->deleteLater();
}

MainWindow::~MainWindow()
{
    delete ui;
}

int Path_Serch::calcG(Point *temp_start ,Point *point)
{
    int extraG=(abs(point->x-temp_start->x)+abs(point->y-temp_start->y))==1?kCost1:kCost2;
    int parentG = point->parent==NULL?0:point->parent->G;
    return extraG+parentG;
}

int Path_Serch::calcH(Point *point,Point *end)
{
    return sqrt((double)(end->x-point->x)*(double)(end->x-point->x)+(double)(end->y-point->y)*(double)(end->y-point->y))*kCost1;
}

int Path_Serch::calcF(Point *point)
{
    return point->G+point->H;
}

Point *Path_Serch::getLeastFpoint()
{
    if(!openList.empty())
    {
        auto resPoint=openList.front();
        for(auto &point:openList)
            if(point->F<resPoint->F)
            {
                resPoint=point;
            }
        return resPoint;
    }
    return NULL;
}

Point *Path_Serch::findPath(Point &startPoint,Point &endPoint,bool isIgnoreCorner)
{
    openList.push_back(new Point(startPoint.x,startPoint.y));
    while(!openList.empty())
    {
        auto curPoint=getLeastFpoint(); //找到F值最小的点
        openList.remove(curPoint); //从开启列表中删除
        closeList.push_back(curPoint); //放到关闭列表
        //1.找到当前周围八个格中可以通过的格子
        auto surroundPoints=getSurroundPoints(curPoint,isIgnoreCorner);
        for(auto &target:surroundPoints)
        {
            //2.对某一个格子，如果它不在开启列表中，加入到开启列表，设置当前格为其父节点，计算F G H
            if(!isInList(openList,target))
            {
                target->parent=curPoint;
                target->G=calcG(curPoint,target);
                target->H=calcH(target,&endPoint);
                target->F=calcF(target);
                arr[target->x][target->y]=3;
                openList.push_back(target);
            }
            else//3.对某一个格子，它在开启列表中，计算G值, 如果比原来的大, 就什么都不做, 否则设置它的父节点为当前点,并更新G和F
            {
                int tempG=calcG(curPoint,target);
                if(tempG<target->G)
                {
                    target->parent=curPoint;
                    target->G=tempG;
                    target->F=calcF(target);
                }
            }
            Point *resPoint=isInList(openList,&endPoint);
            if(resPoint)
            {
                qDebug()<<'('<<resPoint->x<<','<<resPoint->y<<')';
                return resPoint;
            }
            //返回列表里的节点指针，不要用原来传入的endpoint指针，因为发生了深拷贝
        }
    }

    return NULL;
}

std::list<Point *> Path_Serch::GetPath(Point &startPoint,Point &endPoint,bool isIgnoreCorner)
{
    Point *result=findPath(startPoint,endPoint,isIgnoreCorner);
    std::list<Point *> path;
    //返回路径，如果没找到路径，返回空链表
    while(result)
    {
        path.push_front(result);
        result=result->parent;
    }
    return path;
}
Point *Path_Serch::isInList(const std::list<Point *> &list,const Point *point) const
{
    //判断某个节点是否在列表中，这里不能比较指针，因为每次加入列表是新开辟的节点，只能比较坐标
    for(auto p:list)
        if(p->x==point->x&&p->y==point->y)
            return p;
    return NULL;
}

bool Path_Serch::isCanreach(const Point *point,const Point *target,bool isIgnoreCorner) const
{
    if(target->x<0||target->x>50-1||target->y<0||target->y>50-1)
        return false;
    else if(arr[target->x][target->y]==1)
        return false;
    else if(target->x==point->x&&target->y==point->y)
        return false;
    else if(isInList(closeList,target)) //如果点与当前节点重合、超出地图、是障碍物、或者在关闭列表中，返回false
        return false;
    else
    {
        if(abs(point->x-target->x)+abs(point->y-target->y)==1) //非斜角可以
            return true;
        else
        {
            //斜对角要判断是否绊住
            if(arr[point->x][target->y]==0&&arr[target->x][point->y]==0)
                return true;
            else
                return isIgnoreCorner;
        }
    }
}

std::vector<Point *> Path_Serch::getSurroundPoints(const Point *point,bool isIgnoreCorner) const
{
    std::vector<Point *> surroundPoints;

    for(int x=point->x-1;x<=point->x+1;x++)
        for(int y=point->y-1;y<=point->y+1;y++)
            if(isCanreach(point,new Point(x,y),isIgnoreCorner))
                surroundPoints.push_back(new Point(x,y));

    return surroundPoints;
}

void MainWindow::on_pushButton_clicked()
{
    Point start_point(paintX[0],paintY[0]);
    Point end_point(paintX[1],paintY[1]);
    arr[paintX[0]][paintY[0]]=0;
    arr[paintX[1]][paintY[1]]=0;
    Path_Serch p;
    std::list<Point *> path = p.GetPath(start_point,end_point,false);
    for(auto &p:path)
    {
        arr[p->x][p->y]=2;
    }
    update();
}

void MainWindow::on_pushButton_2_clicked()
{
    paintX[0]=temp[0];
    paintY[0]=temp[1];
    if( paintX[0]!=-1 && paintY[0]!=-1)
    {
        update();
        temp[0]=-1;
        temp[1]=-1;
    }
}

void MainWindow::on_pushButton_3_clicked()
{
    paintX[1]=temp[0];
    paintY[1]=temp[1];
    if( paintX[1]!=-1 && paintY[1]!=-1)
    {
        update();
        temp[0]=-1;
        temp[1]=-1;
    }
}

void MainWindow::on_pushButton_4_clicked()
{
    for (int i = 0; i < 50; i++)
    {
        for(int j=0;j<50;j++)
            arr[i][j]=0; // 清空内部向量
    }
    paintX[0]=-1;
    paintX[1]=-1;
    paintY[0]=-1;
    paintY[1]=-1;
    update();

}
